package org.suwenqian.airoleplayingchatbackend.service;

import cn.dev33.satoken.secure.BCrypt;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.suwenqian.airoleplayingchatbackend.common.constant.Constants;
import org.suwenqian.airoleplayingchatbackend.common.constant.GlobalConstants;
import org.suwenqian.airoleplayingchatbackend.common.enums.DeviceType;
import org.suwenqian.airoleplayingchatbackend.common.enums.LoginType;
import org.suwenqian.airoleplayingchatbackend.common.exception.ServiceException;
import org.suwenqian.airoleplayingchatbackend.dao.dto.req.LoginReqDTO;
import org.suwenqian.airoleplayingchatbackend.dao.entity.LoginUser;
import org.suwenqian.airoleplayingchatbackend.dao.entity.UserDO;
import org.suwenqian.airoleplayingchatbackend.dao.mapper.UserMapper;
import org.suwenqian.airoleplayingchatbackend.utils.LoginHelper;
import org.suwenqian.airoleplayingchatbackend.utils.RedisUtils;

import java.time.Duration;
import java.util.function.Supplier;

@Service
@RequiredArgsConstructor
public class SysLoginService {

    @Value("${user.password.maxRetryCount}")
    private Integer maxRetryCount;
    @Value("${user.password.lockTime}")
    private Integer lockTime;

    private final UserMapper userMapper;

    public String login(LoginReqDTO requestParm ) {
        LambdaQueryWrapper<UserDO> eq = Wrappers.lambdaQuery(UserDO.class)
                .eq(UserDO::getUsername, requestParm.getUsername());
        UserDO userDO = userMapper.selectOne(eq);

        checkLogin(LoginType.PASSWORD,  requestParm.getUsername(), () -> !BCrypt.checkpw(requestParm.getPassword(), userDO.getPassword()));
        // 此处可根据登录用户的数据不同 自行创建 loginUser
        LoginUser loginUser = buildLoginUser(userDO);
        // 生成token
        LoginHelper.loginByDevice(loginUser, DeviceType.PC);

        return StpUtil.getTokenValue();
    }

    /**
     * 登录校验
     */
    private void checkLogin(LoginType loginType,  String username, Supplier<Boolean> supplier) {
        String errorKey = GlobalConstants.PWD_ERR_CNT_KEY + username;
        String loginFail = Constants.LOGIN_FAIL;

        // 获取用户登录错误次数(可自定义限制策略 例如: key + username + ip)
        Integer errorNumber = RedisUtils.getCacheObject(errorKey);
        // 锁定时间内登录 则踢出
        if (ObjectUtil.isNotNull(errorNumber) && errorNumber.equals(maxRetryCount)) {
            throw new ServiceException(loginType.getRetryLimitExceed());
        }

        if (supplier.get()) {
            // 是否第一次
            errorNumber = ObjectUtil.isNull(errorNumber) ? 1 : errorNumber + 1;
            // 达到规定错误次数 则锁定登录
            if (errorNumber.equals(maxRetryCount)) {
                RedisUtils.setCacheObject(errorKey, errorNumber, Duration.ofMinutes(lockTime));
                throw new ServiceException(loginType.getRetryLimitExceed());
            } else {
                // 未达到规定错误次数 则递增
                RedisUtils.setCacheObject(errorKey, errorNumber);
                throw new ServiceException(loginType.getRetryLimitCount());
            }
        }

        // 登录成功 清空错误次数
        RedisUtils.deleteObject(errorKey);
    }

    /**
     * 构建登录用户
     */
    private LoginUser buildLoginUser(UserDO userDO) {
        LoginUser loginUser = new LoginUser();
        loginUser.setUserId(String.valueOf(userDO.getId()));
        loginUser.setUsername(userDO.getUsername());
        return loginUser;
    }
}

